package com.woniuxy.authserver.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Arrays;

/**
 * 客户端的认证服务
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Resource
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    // 配置客户端信息（第三方）：内存、数据库
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetailsService);
    }

    @Bean
    public ClientDetailsService clientDetailsService(DataSource dataSource) {
        //在数据库中去获取客户端信息（oauth_client_details表）
        return new JdbcClientDetailsService(dataSource);
    }

    @Resource
    private AuthenticationManager authenticationManager;
    // 开启密码模式，验证用户的账号密码
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager)
                .tokenServices(tokenServices());
    }

    // JWT配置相关
    @Resource
    private TokenStore tokenStore;  // token存储方式
    @Resource
    private JwtAccessTokenConverter jwtAccessTokenConverter;  //jwt转换器
    @Resource
    private ClientDetailsService clientDetailsService; //客户端详情对象

    // 配置token、refreshtoken相关
    private AuthorizationServerTokenServices tokenServices(){
        // 创建服务对象
        DefaultTokenServices services = new DefaultTokenServices();
        // 设置客户端详情服务
        services.setClientDetailsService(clientDetailsService);
        // 支持刷新令牌
        services.setSupportRefreshToken(true);
        // 不重复使用refreshtoken，每次刷新之后只能用新的refreshtoken才能继续刷新
        services.setReuseRefreshToken(false);
        // 设置令牌存储策略
        services.setTokenStore(tokenStore);

        // 设置令牌增强
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter));
        services.setTokenEnhancer(tokenEnhancerChain);

        // 设置令牌过期时间
        services.setAccessTokenValiditySeconds(60000);
        services.setRefreshTokenValiditySeconds(60000);

        return services;
    }

    //设置 /oauth/check_token 端点，通过认证后可访问。
    //该端点对应 CheckTokenEndpoint类，用于校验访问令牌的有效性。
    //在客户端访问资源服务器时，会在请求中带上访问令牌。
    //在资源服务器收到客户端的请求时，会使用请求中的访问令牌，找授权服务器确认该访问令牌的有效性。
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        // 默认是denyAll()：拒绝所有
        oauthServer.checkTokenAccess("permitAll()");
    }
}
